import datetime
import queue
from multiprocessing.managers import BaseManager
from multiprocessing import Queue
from xjlibrary.our_file_dir import BaseDir

curPath = BaseDir.get_file_dir_absolute(__file__)
curPath = BaseDir.get_upper_dir(curPath, -1)
configfile = BaseDir.get_new_path(curPath, "db.ini")


class QueueManager(BaseManager):
    pass


class BaseTask(object):
    def __init_(self):
        pass

    def init_send(self, ip, port, authkey=b'abc'):
        """
        初始化send_task 将整个类作为send时使用该
        初始化步骤
        :param ip:
        :param port:
        :param authkey:
        :return:
        """
        print("监听的ip和端口为:{}:{}".format(ip, port))
        # 发送任务的队列:
        self.task_queue = Queue(300)
        # 接收结果的队列:
        self.result_queue = Queue(300)
        self.register_send()
        self.ip = ip
        # 绑定端口5000, 设置验证码'abc':
        self.manager = QueueManager(address=(self.ip, port), authkey=authkey)
        self.task = None
        self.result = None

    def init_node(self, server_addr, port, authkey=b'abc'):
        """
        作为node使用时使用该初始化步骤
        :return:
        """
        self.register_node()
        # 连接到服务器，也就是运行task_master.py的机器:
        self.server_addr = server_addr
        print('Connect to server %s...' % self.server_addr)
        # 端口和验证码注意保持与task_master.py设置的完全一致:
        self.manager = QueueManager(address=(self.server_addr, port), authkey=authkey)
        self.task = None
        self.result = None

    def register_node(self):
        """
        由于这个QueueManager只从网络上获取Queue，所以注册时只提供名字
        注意 传入的字符串要入send注册的字符串一致
        :return:
        """
        QueueManager.register('get_task_queue')
        QueueManager.register('get_result_queue')

    def register_send(self):
        # 把两个Queue都注册到网络上, callable参数关联了Queue对象:
        QueueManager.register('get_task_queue', callable=self.return_task_queue)
        QueueManager.register('get_result_queue', callable=self.return_result_queue)

    def return_task_queue(self):
        return self.task_queue

    def return_result_queue(self):
        return self.result_queue

    def start(self):
        # 启动Queue:
        self.manager.start()

    def set_task_result_obj(self):
        # 获得通过网络访问的Queue对象:
        self.task = self.manager.get_task_queue()
        self.result = self.manager.get_result_queue()

    def set_task(self, n, block=True, timeout=None):
        """
        向任务队列插入数据
        put()
        如果可选的参数block为True且timeout为空对象（默认的情况，阻塞调用，无超时）。
        如果timeout是个正整数，阻塞调用进程最多timeout秒，如果一直无空空间可用，抛出Full异常（带超时的阻塞调用）。
        如果block为False，如果有空闲空间可用将数据放入队列，否则立即抛出Full异常
        其非阻塞版本为put_nowait等同于put(item, False)
        queue.Full
        :param n:
        :return:
        """
        print(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f"))
        print(n)
        self.task.put(n, block=block, timeout=timeout)

    def get_task(self, block=True, timeout=None):
        """
        从队列中移除并返回一个数据。block跟timeout参数同put方法

        其非阻塞方法为｀get_nowait()｀相当与get(False)
        """
        try:
            result = self.task.get(block=block, timeout=timeout)
            return result
        except ConnectionResetError:
            self.reconn()

    def put_result(self, n, block=True, timeout=None):
        """
        从队列中移除并返回一个数据。block跟timeout参数同put方法

        其非阻塞方法为｀get_nowait()｀相当与get(False)
        """
        try:
            result = self.result.put(n, block=block, timeout=timeout)
            return result
        except ConnectionResetError:
            self.reconn()

    def get_result(self):
        # 从result队列读取结果:
        print('Try get results...')
        try:
            for i in range(10):
                r = self.result.get(timeout=10)
                print('Result: %s' % r)
        except queue.Empty:
            print('task queue is empty.')

    def shutdown(self):
        # 关闭:
        self.manager.shutdown()
        print('master exit.')

    def conn(self):
        self.manager.connect()

    def reconn(self):
        """
        重新链接服务端
        :return:
        """
        self.manager.connect()
        self.set_task_result_obj()


"""
send node调用流程
send
sendtask = SendTask()
sendtask.start()
sendtask.set_task_result_obj()
sendtask.set_task()

node
node = NodeTask()
node.conn()
node.set_task_result_obj()
node.get_result()
"""
